home *** CD-ROM | disk | FTP | other *** search
- // Clock.m, simple clock view
- // Author: Ali T. Ozer, NeXT Developer Support Group
- // Created: May 26, 1989 (for version 0.9)
- // Modified: June 14 and Aug 14, 1989 (for version 1.0)
- //
- // Modifier: Jiro Nakamura, Independent NeXT Developer
- // Modified: May 11, 1990 (for Cassandra)
- //
- // Subclass of view to implement a simple analog or digital clock. This view is
- // pretty generic and can probably be added to any program. Different clock
- // faces can be set through the setBackgroundGray: and setClockGray: methods;
- // you can also specify TIFF files as background images for the clock through
- // the setClockFaceToBitmapNamed: method. Finally you have the option of
- // turning the seconds hand on or off.
- static char authorid[] = "$Author: jiro $";
- static char rcsid[] = "$Id: Clock.m,v 1.3 90/12/03 01:55:16 jiro Exp Locker: jiro $";
-
- #import "Clock.h"
- #import "ClockPS.h" // PSwrap routines
-
- #import <dpsclient/wraps.h>
- #import <appkit/nextstd.h>
- #import <appkit/Application.h>
- #import <appkit/Button.h>
- #import <appkit/Window.h>
- #import <objc/typedstream.h>
- #import <string.h>
- #import <sys/time.h>
- #import "calendar.h"
-
- #define MAX_CVIEW_WIDTH 14
-
- @implementation Clock
- // ShowTime() is the timed entry function called by the
- // timed entry mechanism. It first writes the time out on the face of the
- // clock, and then reinstalls the timed entry if the clock is not showing the
- // seconds. If the seconds are being shown, no need to reinstall the timed
- // entry; a second skipped here and then won't matter. If minutes are being
- // shown, we want to make sure that the minute jumps at the next top of the
- // minute, regardless of how long it took to service the timed entry.
-
- void ShowTime (teNum, now, clock)
- DPSTimedEntry teNum;
- double now;
- id clock;
- {
- [clock display];
- if ([clock showSeconds] == NO) [[clock stopTimedEntry] startTimedEntry:NO];
- }
-
- // newFrame creates the view and initializes the various paramaters. The
- // constants below determine the lengths of the clock hands.
-
- #define HOURRATIO 0.2 /* Hour hand length compared face size */
- #define MINUTERATIO 0.35 /* Minute & seconds hands */
-
- + newFrame:(const NXRect *)frm
- {
- self = [super newFrame:frm];
-
- // Set the default state (no special face, no seconds, grays).
- clockFace = nil;
- showSeconds = NO;
- showDate = NO;
- backgroundGray = NX_LTGRAY;
- clockGray = NX_BLACK;
- [self computeNewSizes];
-
- // Start the time entry. YES indicates that this is the first time.
- [self startTimedEntry:YES];
-
- return self;
- }
-
- // Good idea to get rid of the timed entry while freeing...
-
- - free
- {
- [self stopTimedEntry];
- return [super free];
- }
-
- // startTimedEntry will install the timed entry. If fireASAP is YES, the
- // timed entry is set to fire off as soon as possible (this would be the case
- // at the start of the program, for instance). If fireASAP is NO, then the
- // timed entry is set to fire off in one second (if seconds are being shown)
- // or at the top of the next minute (in anytime between 0 and 60 seconds).
-
- - startTimedEntry:(BOOL)fireASAP
- {
- double fireIn;
-
- if (fireASAP) fireIn = 0.0; // Fire as soon as possible!
- else if (showSeconds) fireIn = 1.0; // Fire in a second (good enough)
- else {
- struct timeval currentTime;
- gettimeofday (¤tTime, NULL);
- fireIn = 60.0 - (currentTime.tv_sec % 60); // Top of the minute
- }
-
- clockTE = DPSAddTimedEntry(fireIn, &ShowTime, self, NX_MODALRESPTHRESHOLD);
-
- return self;
- }
-
- // stopTimedEntry stops the timed entry. Don't call this method unless
- // the timed entry is installed an running.
-
- - stopTimedEntry
- {
- DPSRemoveTimedEntry (clockTE);
- return self;
- }
-
- // drawSelf:: displays the face of the clock, showing the correct time.
- // The seconds hand will be shown only if desired.
-
- - drawSelf:(NXRect *)rects :(int)rectCount
- {
- int min, hour, sec;
- struct tm *localTime;
- struct timeval currentTime;
- static char buf[10];
- extern const char *shortWeekDays[7];
- extern const char *shortMonths[12];
-
- // Additions by Jiro for target/action response
- //Send the <action> to the <target>.
- // Mod Jan 6, 1990
- [target perform:action with:self];
-
- gettimeofday (¤tTime, NULL);
- localTime = localtime (&(currentTime.tv_sec));
- min = localTime->tm_min;
- hour = localTime->tm_hour;
- sec = localTime->tm_sec;
-
- // Erase the background and composite the clock face
- // in if there is one.
- // Note that we erase even though we might have a bitmap;
- // this way if the
- // bitmap is smaller than the view it'll still look OK. In a more
- // efficient frame of mind, one could try to avoid unnecessary fills.
-
- if (backgroundGray >= 0.0 && backgroundGray <= 1.0) {
- PSsetgray (backgroundGray);
- NXRectFill (&bounds);
- }
-
- // If we have a clockface, let's display it
- if(clockFace != nil)
- {
- #ifdef DEBUG
- fprintf(stderr,"%s: Compositing clock face.\n",
- PROGNAME);
- #endif
- [clockFace composite:NX_COPY toPoint:&compositeTo];
- }
-
- switch( clockType)
- {
- case CLOCK_ANALOG:
- // Display the seconds arm, if desired, and then
- // the hours and minutes.
- if (showSeconds)
- drawClockHand (-6.0 * sec, minuteLen,
- clockGray, 0.0);
- drawClockHand (- (hour + min / 60.0) * 30.0, hourLen,
- clockGray, 2.0);
- drawClockHand (- fmod(min, 60.0) * 6.0, minuteLen,
- clockGray, 2.0);
- if( showDate)
- {
- sprintf(buf, "%s %2d",
- shortMonths[localTime->tm_mon],
- localTime->tm_mday);
- drawClockString( 0, 7, 8, buf);
- drawClockString( 0, -10, 8, (char *)
- shortWeekDays[localTime->tm_wday]);
- }
- break;
- case CLOCK_DIGITAL:
- if( !militaryTime)
- {
- if( hour >= 12 )
- hour -= 12;
- if( hour == 0 )
- hour = 12;
- }
-
- sprintf(buf, "%2d:%02d", hour, min);
- drawClockString( 0, 6, 18, buf);
- sprintf(buf,"%s %s %2d", (char *)
- shortWeekDays[localTime->tm_wday],
- shortMonths[localTime->tm_mon], localTime->tm_mday);
- drawClockString( 0, -1, 8, buf);
- PSmoveto( -10, -5);
- PSlineto( 10, -5);
- PSstroke();
- drawClockString( 0, -14, 8, eventTime);
- drawClockString( 0, -21, 8, eventMessage);
-
- break;
- default:
- fprintf(stderr,"%s: Unrecognized clock type: %d\n",
- PROGNAME, clockType);
- }
- return self;
- }
-
- // Set the clock face to the specified bitmap. If bitmapName is NULL,
- // then we have no clock face.
-
- - setClockFaceToBitmapNamed:(const char *)bitmapName
- {
-
- #ifdef DEBUG
- fprintf(stderr, "%s: Setting clockface bitmap to %s\n",
- PROGNAME, bitmapName);
- #endif
-
- if (bitmapName && (clockFace = [NXImage findImageNamed:bitmapName]))
- {
- [self computeBitmapLocation];
- }
- else
- {
- #ifdef DEBUG
- fprintf(stderr,"%s: Couldn't find a icon for %s.\n",
- PROGNAME, bitmapName);
- #endif
- clockFace = nil;
- }
-
- [self display];
-
- return self;
- }
-
- // Compute and cache the location where the clock face should be
- // composited into. This method should be invoked everytime the clock face
- // changes or the view is resized.
- //
- // We assume that the view is translated so that the origin is at the center.
-
- - computeBitmapLocation
- {
- if (clockFace) {
- NXSize bitmapSize;
- [clockFace getSize:&bitmapSize];
- compositeTo.x = - bitmapSize.width / 2.0;
- compositeTo.y = - bitmapSize.height / 2.0;
- }
- return self;
- }
-
-
- // Figure the lengths of the hands (based on the size of the view) and
- // translate the view so that the center is (0,0).
-
- - computeNewSizes
- {
- // Translate view so that the center is 0,0.
- [self setDrawOrigin:-bounds.size.width/2.0 :-bounds.size.height/2.0];
-
- // Compute the hand lengths and cache bitmap location.
- [self computeBitmapLocation];
- minuteLen = MIN(bounds.size.width, bounds.size.height) * MINUTERATIO;
- hourLen = MIN(bounds.size.width, bounds.size.height) * HOURRATIO;
-
- return self;
- }
-
- // Overriding sizeTo:: allows us to resize and fix up the clock whenever
- // the size is changed.
-
- - sizeTo:(NXCoord)w :(NXCoord)h
- {
- [super sizeTo:w :h];
- [self computeNewSizes];
- return self;
- }
-
- // setShowSecondsToBool: sets whether or not the seconds hand is shown.
- // The timed entry must be reinstalled whenever this setting is changed
- // as time to the next firing changes.
-
- - setShowSecondsToBool:(BOOL)seconds
- {
- showSeconds = seconds;
- [[self stopTimedEntry] startTimedEntry:NO];
- [self display];
-
- return self;
- }
-
- - setShowDateToBool: (BOOL) date
- {
- showDate = date;
- [[self stopTimedEntry] startTimedEntry:NO];
- [self display];
-
- return self;
- }
-
- - setMilitaryTimeToBool: (BOOL) mt
- {
- militaryTime = mt;
- [[self stopTimedEntry] startTimedEntry:NO];
- [self display];
-
- return self;
- }
-
- - setClockTypeToInt: (int) type
- {
- clockType = type;
- [[self stopTimedEntry] startTimedEntry:NO];
- [self display];
-
- return self;
- }
-
-
- // Methods to set/get background and clock hand colors.
-
- - setBackgroundGrayToFloat:(float)gray
- {
- backgroundGray = gray;
- [self display];
-
- return self;
- }
-
- - setClockGrayToFloat:(float)gray
- {
- clockGray = gray;
- [self display];
-
- return self;
- }
-
- - setMessage: (char *) buf1 : (char *) buf2
- {
- strncpy( eventTime, buf1, MAX_CVIEW_WIDTH);
- strncpy( eventMessage, buf2, MAX_CVIEW_WIDTH);
-
- if( clockType == CLOCK_DIGITAL)
- [self display];
- return self;
- }
-
- // Methods to return the values of the above parameters
-
- - (BOOL)showSeconds
- {
- return showSeconds;
- }
-
- - (BOOL)showDate
- {
- return showDate;
- }
-
- - (const char *)clockFaceBitmapName
- {
- return [clockFace name];
- }
-
- - (float)backgroundGray
- {
- return backgroundGray;
- }
-
- - (float)clockGray
- {
- return clockGray;
- }
-
- // Finally, the IB-callable (target/action) versions of the above methods.
-
- - setClockFace:sender
- {
- return [self setClockFaceToBitmapNamed:[sender stringValue]];
- }
-
- - setShowSeconds:sender
- {
- return [self setShowSecondsToBool:[sender state]];
- }
-
- - setShowDate:sender
- {
- return [self setShowDateToBool:[sender state]];
- }
-
- - setBackgroundGray:(id)sender
- {
- return [self setBackgroundGrayToFloat:[sender floatValue]];
- }
-
- - setClockGray:(id)sender
- {
- return [self setClockGrayToFloat:[sender floatValue]];
- }
-
- // Additions to allow target/action response
- // Mods by Jiro Nakamura, Jan 6, 1990
- - setTarget: (id) aTarget
- {
- target = aTarget;
- return self;
- }
-
- - setAction: (SEL) anAction
- {
- action = anAction;
- return self;
- }
-
- // Archiving methods.
-
- - awake
- {
- [self computeNewSizes];
- [self startTimedEntry:YES];
- return self;
- }
-
- - write:(NXTypedStream *)stream
- {
- const char *bitmapName = [self clockFaceBitmapName];
-
- [super write:stream];
- NXWriteTypes (stream, "ffi*",
- &backgroundGray, &clockGray, &showSeconds, &bitmapName);
- return self;
- }
-
- - read:(NXTypedStream *)stream
- {
- char *bitmapName;
-
- [super read:stream];
- NXReadTypes (stream, "ffi*",
- &backgroundGray, &clockGray, &showSeconds, &bitmapName);
- [self setClockFaceToBitmapNamed:bitmapName];
- if (bitmapName) free (bitmapName);
-
- return self;
- }
-
- @end
-